package chances.system.log.interceptor;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.Set;

import javax.persistence.EntityManager;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.ObjectUtils;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import com.fasterxml.jackson.databind.ObjectMapper;

import chances.system.log.entity.CompareLog;
import chances.system.operator.entity.Operator;
import chances.system.operator.entity.Oplog;
import chances.system.operator.service.OperatorService;
import chances.system.operator.web.OperatorSession;

/**
 * 对修改方法的拦截，找出修改前和修改后的区别。
 * preHandle从db中load修改前数据。
 * postHandle从db中load修改后的数据。两者相比。
 * @author <a href="mailto:zhangxb@chances.com.cn">zhangxb</a>
 *
 */
public class CompareLogInterceptor extends HandlerInterceptorAdapter {

    private static final String ROWID = "rowId";
    private static final String COMPARE_OBJ = "compareObj";
    private String id;
    @Autowired
    EntityManager entityManager;

    @Autowired
    private OperatorService operatorService;

    ObjectMapper objectMapper = new ObjectMapper();

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
            Object handler) throws Exception {

        if ((handler instanceof HandlerMethod)) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            CompareLog compareLog = method.getAnnotation(CompareLog.class);
            if (compareLog != null) {
                Object result = getObject(request, compareLog);
                request.setAttribute(COMPARE_OBJ, result);
            }
        }
        return true;
    }

    @SuppressWarnings("unchecked")
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

        if ((handler instanceof HandlerMethod)) {
            HandlerMethod handlerMethod = (HandlerMethod) handler;
            Method method = handlerMethod.getMethod();
            CompareLog compareLog = method.getAnnotation(CompareLog.class);

            if (compareLog != null) {
                StringBuffer compareInfo = compare(request, compareLog);
                Operator operator = getOperator(request, compareLog);

                // 指定了objName但是没有request中找到对象说明正确,否则说明错误
                Integer result = Oplog.OPER_SUCCESS;
                String objName = compareLog.objName();
                Object object = null;
                String objType = "";
                if (StringUtils.isNotBlank(objName)) {
                    object = request.getAttribute(compareLog.objName());
                    if (object != null) {
                        objType = object.getClass().getSimpleName().toLowerCase();
                        result = Oplog.OPER_SUCCESS;
                    } else {
                        result = Oplog.OPER_ERROR;
                    }
                }

                String desc = compareLog.desc();
                if (StringUtils.isNotBlank(desc)) {
                    if (object != null) {
                        String json = objectMapper.writeValueAsString(object);
                        Map<String, Object> map = (Map<String, Object>) objectMapper.readValue(json,
                                Map.class);
                        for (String key : map.keySet()) {
                            String value = "";
                            if (map.get(key) != null) {
                                value = map.get(key).toString();
                            }
                            desc = desc.replace("{" + key + "}", value);
                        }
                    } else {
                        desc = "";
                    }
                }

                Oplog oplog = new Oplog(operator, compareLog.operateType(), objType,
                        method.getDeclaringClass().getName(), result, desc);
                oplog.setFormParameters(compareInfo.toString());
                oplog.setObjId(Long.valueOf(id));
                operatorService.save(oplog);
            }
        }
        super.postHandle(request, response, handler, modelAndView);
    }

    private Object getObject(HttpServletRequest request, CompareLog compareLog) {
        Class<?> clazz = compareLog.clazz();
        String idColumn = compareLog.idColumn();
        id = request.getParameter(ROWID);
        String query = getQueryString(clazz, idColumn, id);
        Object result = entityManager.createQuery(query).getSingleResult();
        return result;
    }

    private StringBuffer compare(HttpServletRequest request, CompareLog compareLog)
            throws IllegalAccessException, InvocationTargetException, NoSuchMethodException {
        Object oldObj = request.getAttribute(COMPARE_OBJ);
        request.removeAttribute(COMPARE_OBJ);
        Object currentObj = getObject(request, compareLog);

        Map oldMap = BeanUtils.describe(oldObj);
        Map currentMap = BeanUtils.describe(currentObj);
        Set keySet = oldMap.keySet();
        StringBuffer compareInfo = new StringBuffer();
        for (Object object : keySet) {
            Object oldValue = oldMap.get(object);
            Object currentValue = currentMap.get(object);
            boolean equal = ObjectUtils.equals(oldValue, currentValue);
            if (!equal) {
                compareInfo.append("column [").append(object).append("]");
                compareInfo.append(" from [").append(oldValue).append("]");
                compareInfo.append(" to [").append(currentValue).append("]; ");
            }
        }
        return compareInfo;
    }

    /**
     * 
     * @param request
     * @param compareLog
     * @return
     */
    private Operator getOperator(HttpServletRequest request, CompareLog compareLog) {
        if (request.getAttribute(compareLog.operator()) != null) {
            return (Operator) request.getAttribute(compareLog.operator());
        } else {
            Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
            if (authentication != null) {
                return ((OperatorSession) authentication.getPrincipal()).getOperator();
            } else {
                return null;
            }

        }

    }

    /**
     * 
     * @param clazz
     * @param idColumn
     * @param id
     * @return
     */
    private String getQueryString(Class<?> clazz, String idColumn, String id) {
        StringBuffer hql = new StringBuffer();
        hql.append("from ").append(clazz.getSimpleName());
        hql.append(" where ").append(idColumn).append("=").append(id);
        return hql.toString();
    }

}
